﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

namespace System.Qizx.Xdm {
    internal class XdmReader : IDisposable {

        #region fields
        internal string _baseURI;
        internal XdmDocument _currDocument;
        internal List<string> _discoveredFiles = new List<string>();
        #endregion

        public override string ToString() {
            return "Reader: " + _baseURI;
        }

        #region constructor
        internal XdmReader(string fileName) {
            _baseURI = fileName;

        }
        #endregion


       /// <summary>
        /// Read All nodes into XdmDocument model
        /// </summary>
        /// <param name="document"></param>
        internal void BuildDocument(XdmDocument document) {
            _baseURI = document.BaseURI;
            _currDocument = document;

            Stack<XdmNode> stackNodes = new Stack<XdmNode>();
            stackNodes.Push(_currDocument);
            try {
                XmlReaderSettings setting = new XmlReaderSettings();
                using (XmlReader reader = XmlReader.Create(_baseURI, setting)) {
                    ReadChildren(reader, stackNodes);
                }
            } catch (Exception e) {
                //LogWatch.Error(e);
                System.Console.WriteLine(e.Message);
            }

            stackNodes.Pop();
        }

        internal XdmNode _currParent;

        protected void ReadChildren(XmlReader reader, Stack<XdmNode> stackNodes) {
            XdmNode currParentNode = stackNodes.Peek();
            while (reader.Read()) {
                //System.Console.WriteLine(reader.NodeType + " - " + reader.LocalName);
                switch (reader.NodeType) {
                    case XmlNodeType.Document:
                    case XmlNodeType.Element:
                        XdmElement newElement = CreateElement(reader);
                        currParentNode.AppendChild(newElement);
                        currParentNode = newElement;
                        stackNodes.Push(currParentNode);

                        if (reader.IsEmptyElement) {
                            stackNodes.Pop();
                            currParentNode = stackNodes.Peek();
                        }
                        break;
                    case XmlNodeType.EndElement:
                        stackNodes.Pop();
                        currParentNode = stackNodes.Peek();
                        break;
                    case XmlNodeType.Text:
                        XdmNode textNode = _currDocument.CreateTextNode(reader.Value);
                        currParentNode.AppendChild(textNode);
                        break;
                    case XmlNodeType.Comment:
                        XdmNode commentNode = _currDocument.CreateComment(reader.Value);
                        currParentNode.AppendChild(commentNode);
                        break;
                }
            }
        }

        /// <summary>
        /// Create new element with attributes,
        /// using concrete class for specific element, if no match type, using different default type for element
        /// </summary>
        /// <param name="reader"></param>
        /// <returns></returns>
        protected virtual XdmElement CreateElement(XmlReader reader) {

            XdmElement newElement = new XdmElement(reader.Prefix, reader.LocalName, reader.NamespaceURI, _currDocument);

            #region Read attriutes
            if (reader.HasAttributes) {
                for (int i = 0; i < reader.AttributeCount; i++) {
                    reader.MoveToAttribute(i);
                    XdmAttribute attribute = _currDocument.CreateAttribute(reader.Prefix, reader.LocalName, reader.NamespaceURI);
                    attribute.TextValue = reader.ReadContentAsString();
                    newElement.AddAttribute(attribute);
                }
                reader.MoveToElement(); //HACK move to element get correct IsEmptyElement 
            }
            #endregion

            return newElement;
        }


        public void Dispose() {
            if (_discoveredFiles != null) {
                _discoveredFiles.Clear();
                _discoveredFiles = null;
            }
            _currDocument = null;
            _baseURI = null;
        }
    }
}
